/******************************************************************/
/* 				MOLCLOCK						*/
/* Unlike MINIX where CLOCK is a task, MOLCLOCK is a set of 	*/
/* functions related to system time and alarms managed instead of	*/
/* a Timer interrupt by a Linux ALARM Signal 				*/ 
/******************************************************************/

/*---------------------- LINUX HEADERS ---------------------------*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/*---------------------- MINIX HEADERS ---------------------------*/
#include "../include/minix/config.h" 	
#include "../include/minix/endpoint.h"
#include "../include/minix/com.h"
#include "../../kernel/ipc.h"	

/* #include "../../kernel/config.h" 	*/
/* #include "../../kernel/const.h" 		*/
/* #include "../../kernel/type.h" 		*/

/*---------------------- MOL HEADERS -----------------------------*/
#include "../include/mol/molminix.h"
#define MOLDBG 1
#include "../include/mol/mollib.h"

#define _SYSTEM
#include "../include/mol/molerr.h" 

/*---------------------- MOLKERNEL HEADERS -------------------------*/
#include "molproc.h"
#include "svripc.h"
#include "molclock.h"

extern struct kproc	kproc[NR_PROCS+NR_TASKS];	/* kernel process table */
extern pid_t		kpid;					/* MOLKERNEL Linux PID	*/

/*------------------- GLOBAL VARIABLES ----------------------------*/
long sync_secs = SYNC_SECS;		/* period for the synchronization with Linux 	*/
clock_t realtime;               	/* Minix real time clock 				*/
clock_t next_timeout;			/* realtime that next timer expires 		*/
ipc_t   clk_ipc;				/* defines IPC struct for clock			*/
mol_proc_t  molck;			/* MOL Process data					*/
long sync_count,init_time;


#define mini_notify(to, msg) 		mnx_notify(to, msg, mckp);

/**===========================================================================*
*                             init_clock                                    *
*===========================================================================**/
void init_clock(void)
{
	int rcode;
	mol_proc_t  *mckp;

	mckp = &molck;
	MOLDEBUG("Initializing clock task...\n");
	
	/* keep track of elapsed time */
	/* initial value should be taken from host OS. 	*/
	realtime = 0;
	
	/* some random test alarm deadline 			*/
	next_timeout = TIMEOUT_SECS; 
	
	/* syncronization period with LINUX timer		*/
	init_time = walltime();
	sync_count = sync_secs;
	
	/* Initialize the CLOCK's interrupt hook. 	*/
	set_clock_handler();
	
	/* fills the process struct with pid and name	*/
	mckp->p_pid = kpid;
	strncpy(mckp->p_name,"clock",P_NAME_LEN);

OLDEBUG("%s: Initializing  %d IPC...\n",mckp->p_name,mckp->p_pid);

    /*------- initialize IPC communications --------------*/
	rcode = ipc_open(mckp, MOLKERNEL_ADDR, MOLKERNEL_PORT);
    	if(rcode != OK)
      	MOLERROR("ipc_open: error:%d \n",rcode); 

    /*------- Register the process on MOLKERNEL-------------*/
OLDEBUG("Binding to molminix\n");
    rcode = mnx_bind(mckp);
}


/*============================================================================*
 *                              clock_handler                                	*
* this routine is equivalent to the clock interrupt handler 			*
 *============================================================================*/

static void clock_handler(int sig)
{
	int rcode;

	/* update realtime */
	realtime++;
	
	/* update the syncronization period */
	sync_count--;
	
	/* check next alarm */
	if(next_timeout <= realtime)
		/* Unlike Minix which send a notify message to the clock task	*/
		/* do_clocktick is executed here						*/
		do_clocktick(&clk_ipc);

	/* check if it is time to synchronize with the Linux clock			*/
	if(sync_count == 0)
		{
		sync_count = sync_secs;
		do_synclinux();
		}
}

/*===========================================================================*
 *                              set_clock_handler                            *
 * Sets the ALARM signal handler 								*
 *===========================================================================*/
int set_clock_handler(void)
{
	struct itimerval itv;
	struct sigaction sa;
	int rcode;

OLDEBUG("\n");
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	sa.sa_handler = clock_handler;
	sa.sa_flags = SA_RESTART;
	if ((rcode=sigaction(SIGALRM, &sa, NULL)) == -1)
      	MOLERROR("sigaction rcode=%d\n",rcode);

    	/* Set timer to run every second */
	itv.it_value.tv_sec 	= INTERVAL_SECS;
	itv.it_value.tv_usec 	= INTERVAL_USECS;
	itv.it_interval.tv_sec 	= INTERVAL_SECS;
	itv.it_interval.tv_usec = INTERVAL_USECS;

    	if ((rcode = setitimer(ITIMER_REAL, &itv, 0)) == -1)
        	MOLERROR("setitimer rcode=%d\n", rcode);
	return(OK);
}

/*============================================================================*
 *                              do_clocktick               	               	*
 * Sends a notify() message to SYSTEM task when a Minix alarm has expired	*
 *============================================================================*/
int do_clocktick(ipc_t *pipc)
{
	message msg;
	mol_proc_t  *mckp;

	mckp = &molck;

OLDEBUG("lnx_secs=%ld realtime=%ld\n", walltime()-init_time,realtime);

	/* Alarm timeout. Send NOTIFY to SYSTASK */
	  mini_notify(SYSTEM, &msg); 

	/* reschedule test alarm */
	next_timeout = next_timeout + TIMEOUT_SECS;
}

/*===========================================================================*
*                             do_synclinux                                    *
* Synchronize Minix clock with Linux clock						*
 *============================================================================*/
void do_synclinux(void)
{
	long lnx_secs;
	lnx_secs = walltime()-init_time;
	if( lnx_secs != realtime)
			{
			MOLDEBUG("lnx_secs=%ld realtime=%ld\n",lnx_secs,realtime);
			realtime=lnx_secs;
			}
}

/*===========================================================================*
 *                              walltime		                             *
 * Returns time of day										*
 *===========================================================================*/
long walltime()
{
	struct timeval tv;
	gettimeofday(&tv,NULL);
	return((unsigned long int) tv.tv_sec);
}

/*===========================================================================*
 *				get_uptime							     	*
 * Returns the current clock uptime in ticks. 						*
 *===========================================================================*/
clock_t get_uptime()
{
  return(realtime);
}
